#include "table.h"

 unsigned long hash(char *s, int sz) {
	 int hash = 5381;
	 int i;
	 for(i=0; i<sz;i++,s++) hash = ((hash << 5) + hash) + toupper(*s);
	 return hash;
 }

 
 int table_find(table t, char* key)
 {
	 int keysz = strlen(key);
	 int currentindex = hash(key,keysz) % (t->ctblcap);
	 int ncollisions = 0;
	 while( t->entries[currentindex].type == FULL &&
		 	(strncasecmp(t->entries[currentindex].name,key,keysz)!=0)) {
		currentindex += 2 * ++ncollisions - 1;
		if( currentindex >= t->ctblcap) currentindex -= t->ctblcap;
	}
	return currentindex;
 }
 
 int table_insert(table t, char* key, void* value, size_t size)
 {
	 int keysz = strlen(key);
	 int index = table_find(t,key);
	 if(t->entries[index].type != FULL) {
		 t->entries[index].type = FULL;
		 t->entries[index].name = memcpy(calloc(sizeof(char),keysz+1),key,keysz);
		 t->entries[index].data = (size == 0) ? (int*)value : memcpy(malloc(size),value,size);
		 t->entries[index].size = size;
		 t->ctblsize++;
	 }
	 if(t->ctblsize == t->ctblcap>>1) {
		 int i;
		 int oldcap = t->ctblcap;
		 entry* oldentries = t->entries;
		 t->entries = (entry*)malloc(sizeof(entry)*oldcap*2);
		 t->ctblsize = 0;
		 t->ctblcap = oldcap<<1;
		 for(i=0;i<t->ctblcap;i++) t->entries[i].type = EMPTY;
		 for(i=0;i<oldcap;i++) {
			 if(oldentries[i].type == FULL) {
			 	table_insert(t,oldentries[i].name,oldentries[i].data,oldentries[i].size);
				free(oldentries[i].name);
			 }
		 }
		 free(oldentries);
	 }
	 return index;
 }

 int table_assign(table t, char* key, void* value, size_t size)
 {
	 int keysz = strlen(key);
	 int index = table_find(t,key);
	 if(t->entries[index].size != 0) free(t->entries[index].data);
	 t->entries[index].data = (size == 0) ? (int*)value : memcpy(malloc(size),value,size); 
	 t->entries[index].size = size;
	 return index;
 }

 int table_includes(table t, char* key)
 {
	int keysz = strlen(key);
	int index = table_find(t,key);
	if(t->entries[index].type == EMPTY) return -1;
	return (strncasecmp(t->entries[index].name,key,keysz)==0) ? index : -1;
 }
 
 void* table_retrieve(table t, char* key)
{
	return t->entries[table_find(t,key)].data;
}

 void* table_at(table t, int index)
{
	return t->entries[index].data;
}

 void  table_destroy(table t)
{
	int i;
	for(i=0;i<t->ctblcap;i++) {
		if(t->entries[i].type == FULL) {
			free(t->entries[i].name);
			if(t->entries[i].size != 0) free(t->entries[i].data);
		}
	}
	free(t->entries);
	free(t);
	return;
}
 void  table_print(table t)
{
	int i;
	for(i=0;i<t->ctblcap;i++)
		if(t->entries[i].type==FULL) 
			printf("[%d] %s => %x\n",i,t->entries[i].name,(int)t->entries[i].data);
	return;
}

 table table_init(size_t capacity)
 {  int i;
 	table t = (table)malloc(sizeof(struct ctbl));
	if( t == NULL) { printf("table_init(): out of memory.\n"); exit(-1); }
	t->entries = (entry*)malloc(sizeof(entry)*capacity);
	if( t->entries == NULL ) {printf("table_init(): out of memory.\n"); exit(-1); }
	for(i=0;i<capacity;i++) t->entries[i].type = EMPTY;
	t->ctblcap = capacity;
	t->ctblsize = 0;

	return t;
 }
